#include "evm_module.h"
#include <winsock2.h>
#include "pthread.h"

#include "evmlist.h"

typedef struct tcp_struct {
    int sock;
    evm_event_msg_t *msg_data;
    evm_event_msg_t *msg_close;
    evm_event_msg_t *msg_end;
    evm_event_msg_t *msg_error;
    pthread_t thread_id;
}tcp_struct_t;

static list_t *tcplist = NULL;

static tcp_struct_t* getObjBySock(int sock){
    int len = list_len(tcplist);
    for(int i=0; i<len; i++){
        tcp_struct_t *tcp ;
        if(list_get(tcplist, i, &tcp)){
            if(tcp->sock == sock){
                return tcp;
            }
        }
    }
    return NULL;
}


static void *callback(void *args)
{
    int res = -1;
    tcp_struct_t *tcp = (tcp_struct_t *)args;
    if(tcp == NULL){
        pthread_exit(NULL);
        return NULL;
    }
    evm_event_msg_t *msg_data = NULL, *msg_end = NULL;

    uint8_t *pbufdata = evm_malloc(512);
    if(pbufdata == NULL){
        evm_module_msg_clear(msg_data);
        evm_module_msg_clear(msg_end);
        return NULL;
    }
    while(1){
        if(tcp->msg_data!=NULL){
            if(msg_data == NULL){
                msg_data = evm_module_msg_create();
                if(msg_data == NULL)
                    break;
                memcpy(msg_data, tcp->msg_data, sizeof(evm_event_msg_t));
            }

            res = recv(tcp->sock, (uint8_t*)pbufdata, 512, 0);
            if (res > 0) {
                msg_buffer_t *buffer = evm_malloc(sizeof(msg_buffer_t));
                if(buffer == NULL)
                    break;
                buffer->data = evm_malloc(res);
                if(buffer->data == NULL){
                    evm_free(buffer);
                    break;
                }

                buffer->len = res;
                memcpy(buffer->data, pbufdata, buffer->len);
                tcp_callback_t* tcpdata = evm_malloc(sizeof (tcp_callback_t));
                if(tcpdata == NULL){
                    evm_free(buffer->data);
                    evm_free(buffer);
                    break;
                }

                tcpdata->buffer = buffer;
                tcpdata->type = msg_data->event;
                msg_data->data = tcpdata;
                evm_module_msg_put(msg_data, 0);
            }
        }

//        if(tcp->msg_end!=NULL){
//            if(msg_end == NULL){
//                msg_end = evm_adaptor_msg_create();
//                if(msg_end == NULL)
//                    break;
//                memcpy(msg_end, tcp->msg_end, sizeof(evm_event_msg_t));
//            }
////			struct lwip_sock *sock = lwip_socket_dbg_get_socket(tcp->sock);
////			enum tcp_state state = sock->conn->pcb.tcp->state;
////			if(state == CLOSE_WAIT){
////				evm_adaptor_msg_put(msg_end, 0);
////				break;
////			}
//        }
        usleep(5000);
    }
    pthread_exit(NULL);
}



static int evm_adaptor_tcp_connect(const char *ip, int port){

    WSADATA wsaData;
    WORD wVersionRequested	=	MAKEWORD(2,2);//create 16bit data
    int err	=	WSAStartup(wVersionRequested,&wsaData);	//load win socket
    if(err!=0)
    {
        return -1;
    }

    SOCKET sockClt = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sockClt == INVALID_SOCKET){
        WSACleanup();
        return -1;
    }

//    unsigned long Opt = 1;
//    ioctlsocket(sockClt, FIONBIO, &Opt);
    size_t ip_len = strlen(ip);
    char *ipbuff = evm_malloc( + 1);
    if(ipbuff == NULL){
        WSACleanup();
        return -1;
    }
    memcpy(ipbuff, ip, ip_len);
    ipbuff[ip_len] = 0;

    SOCKADDR_IN addrSrv;
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_addr.s_addr = inet_addr(ipbuff);
    addrSrv.sin_port = htons(port);

    err = connect(sockClt,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
    evm_free(ipbuff);

    if(err == INVALID_SOCKET)
    {
        WSACleanup();
        evm_print("socket() fail:%d\r\n",WSAGetLastError());
        return -1;
    }

    return sockClt;
}

static int evm_adaptor_tcp_send(int fd, const char *data, int len){
    if(send(fd, data, len, 0) < 0)
        return -1;
    return 0;
}

static int evm_adaptor_tcp_on(int fd, void *param){
    evm_print("evm_adaptor_tcp_on fd:%d\r\n", fd);

    evm_event_msg_t *msg = (evm_event_msg_t *)param;

    tcp_struct_t *tcp = getObjBySock(fd);
    if(tcp == NULL){
        if(tcp == NULL){
            tcp = evm_malloc(sizeof(tcp_struct_t));
            if(tcp == NULL)
                return -1;
        }
        memset(tcp, 0, sizeof(tcp_struct_t));
        tcp->sock = fd;

        if(strcmp(msg->event, "data") == 0){
            tcp->msg_data = msg;
        }else if(strcmp(msg->event, "end") == 0){
            tcp->msg_end = msg;
        }else if(strcmp(msg->event, "close") == 0){
            tcp->msg_close = msg;
        }else if(strcmp(msg->event, "error") == 0){
            tcp->msg_error = msg;
        }

        pthread_t hd;
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr,  PTHREAD_CREATE_DETACHED);
        if ((pthread_create(&hd, &attr, callback, tcp)) == -1)
        {
            printf("create error!\n");
            evm_free(tcp);
            return -1;
        }

        tcp->thread_id = hd;
        list_append(tcplist, tcp);
    }else{
        if(strcmp(msg->event, "data") == 0){
            tcp->msg_data = msg;
        }else if(strcmp(msg->event, "end") == 0){
            tcp->msg_end = msg;
        }else if(strcmp(msg->event, "close") == 0){
            tcp->msg_close = msg;
        }else if(strcmp(msg->event, "error") == 0){
            tcp->msg_error = msg;
        }
    }

    return 0;
}

int evm_adaptor_tcp_close(int fd){
    if(closesocket(fd) == 0){
        tcp_struct_t *tcp = getObjBySock(fd);
        evm_module_msg_put(tcp->msg_close, 0);
        return 0;
    }
    return -1;
}

static int evm_adaptor_tcp_init(void){
    if(tcplist != NULL)
        evm_free(tcplist);
    tcplist = evm_malloc(sizeof (list_t));
    if(tcplist == NULL)
        return -1;
    list_init(tcplist);
    return 0;
}

static void evm_adaptor_tcp_destory(void){
    if(tcplist == NULL)
        return;
    int len = list_len(tcplist);
    tcp_struct_t *tcp;
    for(int i=len; i>=0; i--){
        if(list_get(tcplist, i, &tcp)){
            int sock = tcp->sock;
            evm_adaptor_tcp_close(sock);
        }
    }
    evm_free(tcplist);
}

static evm_tcp_t *s_dev_tcp = NULL;
static bool s_tcp_flag = false;
evm_tcp_t *evm_module_tcp_getInstance(void){
    return s_dev_tcp;
}

int evm_module_tcp_init(void){
    if(s_tcp_flag)
        return 0;
    s_dev_tcp = evm_malloc(sizeof(evm_tcp_t));
    if(s_dev_tcp == NULL)
        return -1;

    s_dev_tcp->connect = evm_adaptor_tcp_connect;
    s_dev_tcp->send = evm_adaptor_tcp_send;
    s_dev_tcp->on = evm_adaptor_tcp_on;
    s_dev_tcp->close = evm_adaptor_tcp_close;

    s_tcp_flag = true;

    evm_adaptor_tcp_init();
    return 0;
}

void evm_module_tcp_destory(void){
    evm_adaptor_tcp_destory();
    s_tcp_flag = false;
    if(s_dev_tcp == NULL)
        evm_free(s_dev_tcp);
    s_dev_tcp = false;
}
